/* Jorge e Alice, para compilar este, usem gcc -lrt -o arq arq.c 
 For a program using message queues to run, the message queue server must first be running.  '-l'.*/

#include <fcntl.h>
#include <sys/stat.h>

//POSIX Messages: <mqueue.h>
#include <mqueue.h>

#include <stdio.h>
#include <wait.h>
#include <unistd.h>

#include <string.h>
#include <stdlib.h>

#include<errno.h> 


#define DEFAULT_PRIO 10
#define BUF_SIZE 256

#define PARENT_TO_CHILD_QUEUE "/msgs-pc"
#define CHILD_TO_PARENT_QUEUE "/msgs-cp"

/**
 * Verifico se a fila ja existia anteriormente,
 * caso sim. removo-a, permitindo sua criacao do zero.
 */

void check_queue(const char* queue_name)

//mq_open() -- Connects to, and optionally creates, a named message queue.
{
  if (mq_open(queue_name, O_RDONLY) >= 0) {
    //mq_unlink() -- Ends the connection to an open message queue 
    //and causes the queue to be removed when the last process closes it.
    if (mq_unlink(queue_name) == -1)
    {
      perror("mq_unlink");
      exit(1);
    } 
  }
}

void prepare_queue_attr(struct mq_attr* mqa)
{
  mqa->mq_maxmsg = 5;
  mqa->mq_msgsize = BUF_SIZE;
}

int main (void)
{
  int status, id, j = 0;
  
  char buf[BUF_SIZE];
  bzero(buf, BUF_SIZE);
  
  check_queue(PARENT_TO_CHILD_QUEUE);
  check_queue(CHILD_TO_PARENT_QUEUE);
  
  // mq_setattr() -- Set or get message queue attributes.
  struct mq_attr mqa;
  prepare_queue_attr(&mqa);
  
  pid_t pid_corrente = getpid();
  printf("PID do processo corrente: %d\n", pid_corrente);
  
  pid_t pid = fork();
   
  if (pid == -1)
  {
     fprintf(stderr, "Erro ao enviar 'fork()'. Falta de recurso..., erro %d\n",errno);
     exit(EXIT_FAILURE);
  }
  
  if (pid > 0)
  {
      //Faça com que o processo pai execute este trecho de código
      // Mostre na console o PID do processo pai e do processo filho
         printf("Pai: %d; Filho: %d\n", getpid(), pid);
	 
	  /*
     * No processo pai 'pc_queue' eh somente escrita
     * enquanto 'cp_queue' eh somente leitura.
     */
	  
	mqd_t pc_queue = mq_open(PARENT_TO_CHILD_QUEUE, O_WRONLY | O_CREAT, 0777, &mqa);
	mqd_t cp_queue = mq_open(CHILD_TO_PARENT_QUEUE, O_RDONLY | O_CREAT, 0777, &mqa);
	
	if (pc_queue == -1)
	{
	  perror("pc_queue -> mq_open"); return 1;
	}
	if (cp_queue == -1)
	{
	  perror("cp_queue -> mq_open"); return 1;
	}
	
	// Monte uma mensagem e a envie para o processo filho
	strcpy(buf, "Clack");
	
	// mq_send() -- Places a message in the queue.
	if (mq_send(pc_queue, buf, BUF_SIZE, DEFAULT_PRIO) == -1)
	{
	  perror("pc_queue -> mq_send");
	  return 1;
	}
	
	// Mostre na tela o texto da mensagem enviada
	printf("Pai: Enviando msg ao processo filho: %s\n", buf);
	
	// mq_receive() -- Receives (removes) the oldest, highest priority message from the queue.
	if (mq_receive(cp_queue, buf, BUF_SIZE, NULL) == -1)
	{
	  perror("mq_receive");
	  return 1;
	}
	
	// Aguarde a resposta do processo filho
	// Mostre na tela o texto recebido do processo filho
	printf("Pai: Mensagem recebida do processo filho: %s\n", buf);

	if (mq_receive(cp_queue, buf, BUF_SIZE, NULL) == -1)
	{
	  perror("mq_receive");
	  return 1;
	}
	// Aguarde mensagem do filho e mostre o texto recebido
	printf("P: Segunda mensagem recebida do processo filho: %s\n", buf);
	 
	  // Aguarde o término do processo filho
	 puts("Esperando filho...");
	 waitpid(pid, NULL, 0);
	 // Informe na tela que o filho terminou e que o processo pai também
	 printf("Pai: Filho terminou, vou terminar tambem\n");
      
  }
  else
  {
      // Faça com que o processo filho execute este trecho de código
      // Mostre na tela o PID do processo corrente e do processo pai
         printf("Filho: %d; Pai: %d\n", getpid(), getppid());
	 
	// Aguarde a mensagem do processo pai e ao receber mostre o texto na tela
	mqd_t pc_queue = mq_open(PARENT_TO_CHILD_QUEUE, O_RDONLY | O_CREAT, 0777, &mqa);
	mqd_t cp_queue = mq_open(CHILD_TO_PARENT_QUEUE, O_WRONLY | O_CREAT, 0777, &mqa);
	
	if (pc_queue == -1)
	{
	  perror("pc_queue -> mq_open"); _exit(1);
	}
	if (cp_queue == -1)
	{
	  perror("cp_queue -> mq_open"); _exit(1);
	}
    
	if (mq_receive(pc_queue, buf, BUF_SIZE, NULL) == -1)
	{
	  perror("pc_queue -> mq_receive");
	  _exit(1);
	}
	
	printf("F: Mensagem recebida do pai: %s\n", buf);
    
	strcpy(buf, "Booommm");
	if (mq_send(cp_queue, buf, BUF_SIZE, DEFAULT_PRIO) == -1)
	{
	  perror("cp_queue -> mq_send");
	  _exit(1);
	}
	
	// Envie uma mensagem resposta ao pai
	printf("F: Mensagem enviada para o pai: %s\n", buf);
    
	bzero(buf, BUF_SIZE); /* zerando o buffer para nao pegar lixo */
	 
	// Execute o comando “for” abaixo
	for (j = 0; j <= 10000; j++);
	
	// Envie mensagem ao processo pai com o valor final de “j”
	printf("Filho: Pai, j = %d\n", j);
	
	 sprintf(buf, "%d", j);
    
	if (mq_send(cp_queue, buf, BUF_SIZE, DEFAULT_PRIO) == -1)
	{
	  perror("cp_queue -> mq_send");
	  _exit(1);
	}
	
	// Execute o comando abaixo e responda às perguntas
	execl("/bin/ls", "ls", NULL);
	// O que acontece após este comando?
	printf("Filho: Apos o execl(), nao deve aparecer nunca...\n");
	// O que pode acontecer se o comando “execl” falhar?
  }
  return 0;
}
